package demoMod.gdxform.ui;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import demoMod.gdxform.abstracts.Container;
import demoMod.gdxform.core.AbstractAction;
import demoMod.gdxform.core.FormManager;
import demoMod.gdxform.enums.GFrameCloseStrategy;
import demoMod.gdxform.enums.GFrameScaleStatus;
import demoMod.gdxform.enums.GFrameWindowMode;
import demoMod.gdxform.enums.GFrameWindowStyle;
import demoMod.gdxform.interfaces.*;

import java.util.ArrayList;
import java.util.List;

public class GFrame extends Container<Element> implements MouseEventSubscriber, KeyboardEventSubscriber {
    private String title;
    private Color titleActivatedColor = Color.BLACK.cpy();
    private Color titleDeactivatedColor = Color.GRAY.cpy();
    private Color borderActivatedColor = new Color(0.05882F, 0.33725F, 0.98431F, 1);
    private Color borderDeactivatedColor = Color.GRAY.cpy();
    private Container<Element> banner;
    private GLabel lblTitle;
    private BitmapFont bannerFont;
    private float bannerHeight = 25;
    private boolean mouseDown = false;
    private boolean dragWindow = false;
    private int priority;
    private GFrameCloseStrategy closeStrategy = GFrameCloseStrategy.EXIT_ON_CLOSE;
    private GFrameScaleStatus scaleStatus = GFrameScaleStatus.NORMAL;
    private boolean waitingUserPerformClose = true;
    private final GFrameWindowStyle windowStyle;
    private GFrameWindowMode windowMode = GFrameWindowMode.NON_MODAL;

    public GFrame(float x, float y, float width, float height, BitmapFont bannerFont) {
        this(x, y, width, height, bannerFont, GFrameWindowStyle.CLASSICAL, false);
    }

    public GFrame(float x, float y, float width, float height, BitmapFont bannerFont, GFrameWindowStyle windowStyle, boolean isSubWindow) {
        setBackground(new Color(0.16863F, 0.16863F, 0.16863F, 1));
        setX(x);
        setY(y);
        setWidth(width);
        setHeight(height);

        setBorderWidth(2);
        setBorder(borderActivatedColor);

        this.priority = incr.getAndIncrement();

        this.bannerFont = bannerFont;
        this.windowStyle = windowStyle;
        initializeBanner();
        if (!isSubWindow) {
            FormManager.getInstance().addContainer(this);
        }
    }

    private class Banner extends Container<Element> implements MouseEventSubscriber {
        Banner() {
            lblTitle = new GLabel(0, getHeight() - GFrame.this.bannerHeight / 2.0F, getWidth(), GFrame.this.bannerFont.getLineHeight(), GFrame.this.bannerFont);
            if (windowStyle != GFrameWindowStyle.NONE) {
                lblTitle.setText(GFrame.this.title);
                setTitleColor(Color.BLACK);
            }
            this.addElement(lblTitle);

            this.setBackground(Color.WHITE);
            GFrame.this.addElement(this);

            GButton min = new GButton(getParent().getWidth() - Gdx.graphics.getWidth() * 0.03125F * 3, 0, Gdx.graphics.getWidth() * 0.03125F, bannerHeight, GFrame.this.bannerFont) {
                @Override
                public void resize(float deltaX, float deltaY, boolean fromParent) {
                    setX(getParent().getWidth() - getWidth() * 3);
                }

                @Override
                public void onClick() {
                    if (scaleStatus == GFrameScaleStatus.MINIMIZE) {
                        actionManager.addToSequenceBot(getScaleAction(GFrameScaleStatus.NORMAL));
                    } else {
                        actionManager.addToSequenceBot(getScaleAction(GFrameScaleStatus.MINIMIZE));
                    }
                    actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
                        GFrameScaleStatus beforeScale = scaleStatus;
                        if (scaleStatus == GFrameScaleStatus.MINIMIZE) {
                            scaleStatus = GFrameScaleStatus.NORMAL;
                        } else {
                            scaleStatus = GFrameScaleStatus.MINIMIZE;
                        }
                        refreshElementsOnScaleStatusChange(beforeScale);
                        return true;
                    }).build());
                }
            };
            min.setBackground(Color.WHITE);
            min.setText("_");
            min.setColor(Color.BLACK);
            min.setId("MinButton");

            GButton max = new GButton(getParent().getWidth() - Gdx.graphics.getWidth() * 0.03125F * 2, 0, Gdx.graphics.getWidth() * 0.03125F, bannerHeight, GFrame.this.bannerFont) {
                @Override
                public void resize(float deltaX, float deltaY, boolean fromParent) {
                    setX(getParent().getWidth() - getWidth() * 2);
                }

                @Override
                public void onClick() {
                    if (scaleStatus == GFrameScaleStatus.MAXIMIZE) {
                        actionManager.addToSequenceBot(getScaleAction(GFrameScaleStatus.NORMAL));
                        setText("O");
                    } else {
                        actionManager.addToSequenceBot(getScaleAction(GFrameScaleStatus.MAXIMIZE));
                        setText("↙");
                    }
                    actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
                        GFrameScaleStatus beforeScale = scaleStatus;
                        if (scaleStatus == GFrameScaleStatus.MAXIMIZE) {
                            scaleStatus = GFrameScaleStatus.NORMAL;
                        } else {
                            scaleStatus = GFrameScaleStatus.MAXIMIZE;
                        }
                        refreshElementsOnScaleStatusChange(beforeScale);
                        return true;
                    }).build());
                }
            };
            max.setBackground(Color.WHITE);
            max.setText("O");
            max.setColor(Color.BLACK);

            GButton close = new GButton(getParent().getWidth() - Gdx.graphics.getWidth() * 0.03125F, 0, Gdx.graphics.getWidth() * 0.03125F, bannerHeight, GFrame.this.bannerFont) {
                @Override
                public void resize(float deltaX, float deltaY, boolean fromParent) {
                    setX(getParent().getWidth() - getWidth());
                }

                @Override
                public void onClick() {
                    performCloseAction();
                    actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> !waitingUserPerformClose).build());
                }
            };
            close.setBackground(Color.WHITE);
            close.setText("X");
            close.setColor(Color.BLACK);

            if (windowStyle == GFrameWindowStyle.CLASSICAL) {
                this.addElement(min);
                this.addElement(max);
            } else {
                min.dispose();
                max.dispose();
            }
            if (windowStyle == GFrameWindowStyle.CLOSE_BUTTON_ONLY || windowStyle == GFrameWindowStyle.CLASSICAL) {
                this.addElement(close);
            } else {
                close.dispose();
            }
        }

        @Override
        public void render(SpriteBatch sb) {
            if (windowStyle == GFrameWindowStyle.NONE) return;
            super.render(sb);
        }

        @Override
        public float getX(boolean isAbsolute) {
            return GFrame.this.getX(isAbsolute);
        }

        @Override
        public float getY(boolean isAbsolute) {
            return GFrame.this.getY(isAbsolute) + GFrame.this.getHeight() - bannerHeight;
        }

        @Override
        public float getWidth() {
            return GFrame.this.getWidth();
        }

        @Override
        public float getHeight() {
            return bannerHeight;
        }

        @Override
        public void setHeight(float height) {
            super.setHeight(height);
            lblTitle.setY(getHeight() - (getHeight() - GFrame.this.bannerFont.getLineHeight()) / 2.0F);
        }

        @Override
        public boolean isResizable() {
            return false;
        }

        @Override
        public void resize(float deltaX, float deltaY, boolean fromParent) {
            List<Element> elements = getElements();
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof ResizeEventSubscriber) {
                    ((ResizeEventSubscriber) element).resize(deltaX, deltaY, true);
                }
            }
        }

        @Override
        public boolean enabled() {
            return true;
        }

        @Override
        public boolean visible() {
            return GFrame.this.visible() && windowStyle != GFrameWindowStyle.NONE;
        }

        @Override
        public void setVisible(boolean visible) {

        }

        @Override
        public boolean clickDown(int screenX, int screenY, int button) {
            if (!this.visible()) return false;
            List<Element> elements = getElements();
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element).clickDown(screenX, screenY, button)) {
                        break;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean clickUp(int screenX, int screenY, int button) {
            if (!this.visible()) return false;
            List<Element> elements = getElements();
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element).clickUp(screenX, screenY, button)) {
                        break;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean mouseDragged(int screenX, int screenY) {
            if (!this.visible()) return false;
            if (dragWindow) {
                List<Element> elements = getElements();
                for (int i=elements.size() - 1;i >= 0;i--) {
                    Element element = elements.get(i);
                    if (element instanceof MouseEventSubscriber) {
                        if (((MouseEventSubscriber) element).mouseDragged(screenX, screenY)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        @Override
        public boolean mouseMoved(int screenX, int screenY) {
            if (!this.visible()) return false;
            if (screenX >= getX(true) && screenX <= getX(true) + getWidth() &&
                    Gdx.graphics.getHeight() - screenY >= getY(true) && Gdx.graphics.getHeight() - screenY <= getY(true) + getHeight()) {
                List<Element> elements = getElements();
                for (int i = elements.size() - 1; i >= 0; i--) {
                    Element element = elements.get(i);
                    if (element instanceof MouseEventSubscriber) {
                        if (((MouseEventSubscriber) element).mouseMoved(screenX, screenY)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        @Override
        public boolean scrolled(int amount) {
            if (!this.visible()) return false;
            List<Element> elements = getElements();
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element).scrolled(amount)) {
                        return true;
                    }
                }
            }
            return false;
        }

        @Override
        public boolean moveToElementBorder(Element element) {
            if (!this.visible()) return false;
            List<Element> elements = getElements();
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element1 = elements.get(i);
                if (element1 instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element1).moveToElementBorder(element)) {
                        return true;
                    }
                }
            }
            return false;
        }
    }

    @Override
    public boolean keyDown(int keyCode) {
        if (!(this.visible() || this.enabled())) return false;
        if (this.activated) {
            List<Element> elements = getElements();
            for (int i = elements.size() - 1; i >= 0; i--) {
                Element element = elements.get(i);
                if (element instanceof KeyboardEventSubscriber) {
                    if (((KeyboardEventSubscriber) element).keyDown(keyCode)) {
                        return true;
                    }
                }
            }
        }
        return windowMode == GFrameWindowMode.MODAL;
    }

    @Override
    public boolean keyUp(int keyCode) {
        if (!(this.visible() || this.enabled())) return false;
        if (this.activated) {
            List<Element> elements = getElements();
            for (int i = elements.size() - 1; i >= 0; i--) {
                Element element = elements.get(i);
                if (element instanceof KeyboardEventSubscriber) {
                    if (((KeyboardEventSubscriber) element).keyUp(keyCode)) {
                        return true;
                    }
                }
            }
        }
        return windowMode == GFrameWindowMode.MODAL;
    }

    @Override
    public boolean keyTyped(char key) {
        if (!(this.visible() || this.enabled())) return false;
        if (this.activated) {
            List<Element> elements = getElements();
            for (int i = elements.size() - 1; i >= 0; i--) {
                Element element = elements.get(i);
                if (element instanceof KeyboardEventSubscriber) {
                    if (((KeyboardEventSubscriber) element).keyTyped(key)) {
                        return true;
                    }
                }
            }
        }
        return windowMode == GFrameWindowMode.MODAL;
    }

    private void initializeBanner() {
        banner = new Banner();
    }

    private void refreshElementsOnScaleStatusChange(GFrameScaleStatus beforeScale) {
        actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(d -> {
            for (Element element : GFrame.this.getElements()) {
                if (element instanceof ResizeEventSubscriber) {
                    float deltaX = 0;
                    float deltaY = 0;
                    switch (scaleStatus) { //现在的缩放状态
                        case NORMAL:
                            switch (beforeScale) { //之前的缩放状态
                                case MAXIMIZE:
                                    deltaX = GFrame.this.getWidth() - Gdx.graphics.getWidth();
                                    deltaY = GFrame.this.getHeight() - Gdx.graphics.getHeight();
                                    break;
                                case MINIMIZE:
                                    deltaY = GFrame.this.getHeight() - GFrame.this.bannerHeight;
                                    float bannerY = GFrame.this.getY(true) + GFrame.this.getHeight();
                                    if (bannerY > Gdx.graphics.getHeight()) {
                                        GFrame.this.setY(GFrame.this.getY(false) - (bannerY - Gdx.graphics.getHeight()));
                                    }
                                    break;
                            }
                            break;
                        case MINIMIZE:
                            switch (beforeScale) {
                                case MAXIMIZE:
                                    deltaX = GFrame.this.getWidth() - Gdx.graphics.getWidth();
                                    deltaY = GFrame.this.getHeight() - Gdx.graphics.getHeight();
                                    break;
                                case NORMAL:
                                    deltaY = GFrame.this.bannerHeight - GFrame.super.getHeight();
                                    break;
                            }
                            break;
                        case MAXIMIZE:
                            switch (beforeScale) {
                                case MINIMIZE:
                                    deltaX =  Gdx.graphics.getWidth() - GFrame.this.getWidth();
                                    deltaY =  Gdx.graphics.getHeight() - GFrame.this.getHeight();
                                    break;
                                case NORMAL:
                                    deltaX = Gdx.graphics.getWidth() - GFrame.super.getWidth();
                                    deltaY = Gdx.graphics.getHeight() - GFrame.super.getHeight();
                                    break;
                            }
                            break;
                    }
                    ((ResizeEventSubscriber) element).resize(deltaX, deltaY, true);
                }
            }
            return true;
        }).build());
    }

    protected void performCloseAction() {
        actionManager.addToSequenceBot(new AbstractAction.Builder().setActionBody(duration -> {
            waitingUserPerformClose = false;
            executeCloseOperation();
            return true;
        }).build());
    }

    protected void executeCloseOperation() {
        switch (closeStrategy) {
            case EXIT_ON_CLOSE:
                FormManager.getInstance().removeContainer(GFrame.this);
                break;
            case HIDDEN_ON_CLOSE:
                GFrame.this.setVisible(false);
                break;
            case MINIMIZE_ON_CLOSE:
                GButton min = (GButton) banner.getElementById("MinButton");
                min.onClick();
                break;
        }
    }

    protected AbstractAction getScaleAction(GFrameScaleStatus scaleStatus) {
        return new AbstractAction.Builder().setActionBody(duration -> true).build();
    }

    public GFrameCloseStrategy getCloseStrategy() {
        return closeStrategy;
    }

    public void setCloseStrategy(GFrameCloseStrategy closeStrategy) {
        this.closeStrategy = closeStrategy;
    }

    public GFrameScaleStatus getScaleStatus() {
        return scaleStatus;
    }

    public void setScaleStatus(GFrameScaleStatus scaleStatus) {
        this.scaleStatus = scaleStatus;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
        lblTitle.setText(this.title);
    }

    public void setTitleColor(Color color) {
        this.titleActivatedColor = color;
    }

    public void setTitleDeactivatedColor(Color color) {
        this.titleDeactivatedColor = color;
    }

    public Color getBorderActivatedColor() {
        return borderActivatedColor;
    }

    public void setBorderActivatedColor(Color borderActivatedColor) {
        this.borderActivatedColor = borderActivatedColor;
    }

    public Color getBorderDeactivatedColor() {
        return borderDeactivatedColor;
    }

    public void setBorderDeactivatedColor(Color borderDeactivatedColor) {
        this.borderDeactivatedColor = borderDeactivatedColor;
    }

    public GFrameWindowMode getWindowMode() {
        return windowMode;
    }

    public void setWindowMode(GFrameWindowMode windowMode) {
        this.windowMode = windowMode;
        if (windowMode == GFrameWindowMode.MODAL) {
            FormManager.getInstance().moveToTop(this);
        }
    }

    @Override
    public void activate() {
        super.activate();
        this.lblTitle.setColor(this.titleActivatedColor);
        this.setBorder(this.borderActivatedColor);
        FormManager.getInstance().moveToTop(this);
    }

    @Override
    public void deactivate() {
        super.deactivate();
        this.lblTitle.setColor(this.titleDeactivatedColor);
        this.setBorder(this.borderDeactivatedColor);
    }

    public void setBannerBackground(Color color) {
        this.banner.setBackground(color);
    }

    public BitmapFont getBannerFont() {
        return bannerFont;
    }

    public void setBannerFont(BitmapFont bannerFont) {
        this.bannerFont = bannerFont;
        lblTitle.setFont(this.bannerFont);
    }

    public float getBannerHeight() {
        return this.bannerHeight;
    }

    public void setBannerHeight(float bannerHeight) {
        this.bannerHeight = bannerHeight;
        this.banner.setHeight(this.bannerHeight);
    }

    @Override
    public void render(SpriteBatch sb) {
        List<Element> tmpList = new ArrayList<>();
        if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
            tmpList.addAll(this.getElements());
            this.clearElements();
            tmpList.remove(this.banner);
            addElement(this.banner);
        }
        super.render(sb);
        if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
            tmpList.forEach(this::addElement);
        }
    }

    @Override
    public float getX(boolean isAbsolute) {
        switch (this.scaleStatus) {
            case NORMAL:
            case MINIMIZE:
            default:
                return super.getX(isAbsolute);
            case MAXIMIZE:
                return 0;
        }
    }

    @Override
    public float getY(boolean isAbsolute) {
        switch (this.scaleStatus) {
            case NORMAL:
            case MINIMIZE:
            default:
                return super.getY(isAbsolute);
            case MAXIMIZE:
                return 0;
        }
    }

    @Override
    public float getWidth() {
        switch (this.scaleStatus) {
            case NORMAL:
            case MINIMIZE:
            default:
                return super.getWidth();
            case MAXIMIZE:
                return getParent() == null ? Gdx.graphics.getWidth() : getParent().getWidth();
        }
    }

    @Override
    public float getHeight() {
        switch (this.scaleStatus) {
            case NORMAL:
            default:
                return super.getHeight();
            case MINIMIZE:
                return this.bannerHeight;
            case MAXIMIZE:
                return getParent() == null ? Gdx.graphics.getHeight() : getParent().getHeight();
        }
    }

    @Override
    public boolean isResizable() {
        return super.isResizable() && this.scaleStatus == GFrameScaleStatus.NORMAL;
    }

    @Override
    public void resize(float deltaX, float deltaY, boolean fromParent) {
        if (!this.visible()) return;
        super.resize(deltaX, deltaY, fromParent);
        if (!fromParent) this.banner.setWidth(getWidth());
        List<Element> elements = getElements();
        for (int i=elements.size() - 1;i >= 0;i--) {
            Element element = elements.get(i);
            if (element instanceof ResizeEventSubscriber) {
                ((ResizeEventSubscriber) element).resize(deltaX, deltaY, true);
            }
        }
    }

    @Override
    public void dispose() {
        super.dispose();
        if (this.getParent() != null) {
            getParent().removeElement(this);
        }
    }

    private Element currentResizingElement = null;

    @Override
    public boolean clickDown(int screenX, int screenY, int button) {
        if (!this.visible()) return false;
        mouseDown = screenX >= getX(true) && screenX <= getX(true) + getWidth() &&
                Gdx.graphics.getHeight() - screenY >= getY(true) && Gdx.graphics.getHeight() - screenY <= getY(true) + getHeight();
        dragWindow = mouseDown &&
                Gdx.graphics.getHeight() - screenY >= banner.getY(true) && Gdx.graphics.getHeight() - screenY <= banner.getY(true) + banner.getHeight() && button == Input.Buttons.LEFT;
        if (mouseDown) {
            FormManager.getInstance().moveToTop(this);
            List<Element> elements = getElements();
            if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
                elements = banner.getElements();
            }
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof Border && element.isResizable()) {
                    if (((Border) element).atBorder(screenX, Gdx.graphics.getHeight() - screenY)) {
                        currentResizingElement = element;
                        return true;
                    }
                }
                if (element instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element).clickDown(screenX, screenY, button)) {
                        return true;
                    }
                }
            }
            FormManager.getInstance().getEventHooks().setCurrentFocusedElement(this);
            return true;
        }
        return windowMode == GFrameWindowMode.MODAL || mouseDown;
    }

    @Override
    public boolean clickUp(int screenX, int screenY, int button) {
        if (!this.visible()) return false;
        if (mouseDown) {
            mouseDown = false;
            dragWindow = false;
            currentResizingElement = null;
            List<Element> elements = getElements();
            if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
                elements = banner.getElements();
            }
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element).clickUp(screenX, screenY, button)) {
                        break;
                    }
                }
            }
            return true;
        }
        return windowMode == GFrameWindowMode.MODAL;
    }

    @Override
    public boolean mouseDragged(int screenX, int screenY) {
        if (!this.visible()) return false;
        if (dragWindow) {
            setX(getX(false) + Gdx.input.getDeltaX());
            setY(getY(false) - Gdx.input.getDeltaY());
        }
        List<Element> elements = getElements();
        if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
            elements = banner.getElements();
        }
        for (int i=elements.size() - 1;i >= 0;i--) {
            Element element = elements.get(i);
            if (element instanceof MouseEventSubscriber) {
                if (((MouseEventSubscriber) element).mouseDragged(screenX, screenY)) {
                    return true;
                }
            }
        }
        if (currentResizingElement != null) {
            ((ResizeEventSubscriber) currentResizingElement).resize(Gdx.input.getDeltaX(), Gdx.input.getDeltaY(), false);
        }
        return windowMode == GFrameWindowMode.MODAL || dragWindow;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        if (!this.visible()) return false;
        boolean mouseMoved = screenX >= getX(true) && screenX <= getX(true) + getWidth() &&
                Gdx.graphics.getHeight() - screenY >= getY(true) && Gdx.graphics.getHeight() - screenY <= getY(true) + getHeight();
        if (mouseMoved) {
            List<Element> elements = getElements();
            if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
                elements = banner.getElements();
            }
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element).mouseMoved(screenX, screenY)) {
                        return true;
                    }
                }
            }
        }
        return windowMode == GFrameWindowMode.MODAL || mouseMoved;
    }

    @Override
    public boolean scrolled(int amount) {
        if (!this.visible()) return false;
        boolean scrolled = Gdx.input.getX() >= getX(true) && Gdx.input.getX() <= getX(true) + getWidth() &&
                Gdx.graphics.getHeight() - Gdx.input.getY() >= getY(true) && Gdx.graphics.getHeight() - Gdx.input.getY() <= getY(true) + getHeight();
        if (scrolled) {
            List<Element> elements = getElements();
            if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
                elements = banner.getElements();
            }
            for (int i=elements.size() - 1;i >= 0;i--) {
                Element element = elements.get(i);
                if (element instanceof MouseEventSubscriber) {
                    if (((MouseEventSubscriber) element).scrolled(amount)) {
                        return true;
                    }
                }
            }
        }

        return windowMode == GFrameWindowMode.MODAL || scrolled;
    }

    @Override
    public boolean moveToElementBorder(Element element) {
        if (!this.visible()) return false;
        List<Element> elements = getElements();
        if (this.getScaleStatus() == GFrameScaleStatus.MINIMIZE) {
            elements = banner.getElements();
        }
        for (int i=elements.size() - 1;i >= 0;i--) {
            Element element1 = elements.get(i);
            if (element1 instanceof MouseEventSubscriber) {
                if (((MouseEventSubscriber) element1).moveToElementBorder(element)) {
                    return true;
                }
            }
        }
        return windowMode == GFrameWindowMode.MODAL;
    }

    @Override
    public int getPriority() {
        return windowMode == GFrameWindowMode.MODAL ? Integer.MAX_VALUE : this.priority;
    }

    @Override
    public void setPriority(int priority) {
        this.priority = priority;
    }

    @Override
    public boolean hasFocus() {
        return true;
    }
}
